package org.jboss.flowlet.rest;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

import org.jboss.flowlet.rest.framework.FileCopy;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.db.GraphSession;
import org.jbpm.file.def.FileDefinition;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;

/**
 * Flowlet - Making jBPM REST
 * This is the RestService controller to handle the process execution for jBPM 3.2.x
 * @author Edgar Silva
 * 
 * */



@Provider
@Path("/")
public class RestService implements Serializable { 
	private static final long serialVersionUID = -550878933446485830L;
	
	@SuppressWarnings("unchecked")
	Enumeration params;
	
	protected ProcessDefinition processDefinition;

	
	
	@GET
	@Path("/")
	@Produces("text/html")
	public String home(){
		
		List<java.lang.reflect.Method> methods = Arrays.asList(this.getClass().getDeclaredMethods());
		
		StringBuilder b = new StringBuilder();
		
		b.append("<html><title>Flowlet - A REST API over BPM</title><head><style>.x{font-family: verdana,ari; color: steelblue; font-size: small;;}" +
				"</style></head><body><font face='Verdana' color=''><h3>Flowlet - Process Management via REST</h3>" +
				"<hr size=1><p> List of Actions:<ul>");
		
        for (java.lang.reflect.Method m : methods) {
        	
        	if (null != m.getAnnotation(Path.class)) {
        		
        		Path p = m.getAnnotation(Path.class);
        		
        		b.append(  String.format("<li class='x'><b>%s</b> with annotation: <a href='jbpm%s'><u>%s</u> </a> </li>\n", m.getName(), p.value() ,p.value()) );
        		
        	}
   			
		}
        b.append("</ul><hr size=1><small>Flowlet - Is member of Breakingwoods Project</small></a></body></html>");
		
		return b.toString();
	}
	
	
	
	
	@GET
	@Path("/start/process/{processdefinition}/{user}")
	@Produces("text/plain")
	public String startProcesInstance(
			@PathParam("processdefinition")String processDefintion, 
			@PathParam("user")String user) {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		try {
			ProcessInstance instance = ctx.newProcessInstance(processDefintion);
			instance.getContextInstance().setVariable("user", user);
			Token t = instance.getRootToken();
			t.signal();
		    ctx.save(instance);

			return new Long(instance.getId()).toString();
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

	}
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/id/{definitionid}/instances")
	@Produces("text/plain")
	public String getProcessInstancesById(
			@PathParam("definitionid")String id) {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		StringBuilder b = new StringBuilder();
		
		try {
			List<ProcessInstance> findProcessInstances = ctx.getGraphSession().findProcessInstances(new Long(id) );
			
			List<org.jbpm.graph.exe.ProcessInstance> list =   findProcessInstances;
			
			for (ProcessInstance processInstance : list) {
				
				b.append(String.format("Instance:%s, Process Definition: %s, Version:%s Started: %s \n", String.valueOf(processInstance.getId()), processInstance.getProcessDefinition().getName(),
						                                                                     processInstance.getProcessDefinition().getVersion(),processInstance.getStart()));
			}
		
			

			return b.toString() ;
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

	}
	
@GET
@Path("/process/{id}/add/var")
@Produces("text/plain")
public String addVariables(@PathParam("id")String id, @Context HttpServletRequest request) {
	 JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();

	try
	{
	GraphSession graphSession = ctx.getGraphSession();
	ProcessInstance processInstance =
	graphSession.loadProcessInstance((new
	Long(id)).longValue());

	params = request.getParameterNames();

	String param;

	StringBuilder b = new StringBuilder();

	while
	(params.hasMoreElements()) {
	param = (String) params.nextElement();


	processInstance.getContextInstance().setVariable(param,
	request.getParameter(param));

	b.append(String.format("Param:%s=%s is Stored in BPM Context\n",param,request.getParameter(param)));
	}

	ctx.save(processInstance);



	return

	b.toString();



	} catch
	(Exception e ) {


	e.printStackTrace();


	return
	"ERROR";


	}


	finally
	{


	ctx.close();


	}

	}
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/name/{definition}/instances")
	@Produces("text/plain")
	public String getProcessInstancesByProcessName(@PathParam("definition")String id) {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		StringBuilder b = new StringBuilder();
		
		try {
			
	
			List<ProcessInstance> findProcessInstances = ctx.getGraphSession().findProcessInstances(ctx.getGraphSession().findLatestProcessDefinition(id).getId());
			
			List<org.jbpm.graph.exe.ProcessInstance> list =   findProcessInstances;
			
			for (ProcessInstance processInstance : list) {
				
				b.append(String.format("Instance:%s, Version:%s Started: %s \n", String.valueOf(processInstance.getId()),
						                                                         processInstance.getProcessDefinition().getVersion(),processInstance.getStart()));
			}
		
			

			return b.toString() ;
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

	}
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/stats/")
	@Produces("text/plain")
	public String getProcessStats() {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		StringBuilder b = new StringBuilder();
		
		try {
			
			List<ProcessDefinition> processDefinitions = ctx.getGraphSession().findAllProcessDefinitions();
			
			for (ProcessDefinition def : processDefinitions) {
				
				List<ProcessInstance> findProcessInstances = ctx.getGraphSession().findProcessInstances(def.getId());
				
				List<org.jbpm.graph.exe.ProcessInstance> list =   findProcessInstances;
				
				for (ProcessInstance processInstance : list) {
					
					b.append(String.format("%s,%s V%s,Started: %s \n", String.valueOf(processInstance.getId()),
							                                                         processInstance.getProcessDefinition().getName(),
							                                                         processInstance.getProcessDefinition().getVersion(),
							                                                         processInstance.getStart()));
				}
				
				
			}

			return b.toString() ;
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

	}
	
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/stats/graph")
	@Produces("text/plain")
	public String getProcessStatsforGraph() {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		StringBuilder b = new StringBuilder();
		b.append("Categories,Number of Instances,Process\n");
		
		try {
			
			List<ProcessDefinition> processDefinitions = ctx.getGraphSession().findAllProcessDefinitions();
			
			

			for (ProcessDefinition def : processDefinitions) {
				
				List<ProcessInstance> findProcessInstances = ctx.getGraphSession().findProcessInstances(def.getId());
				
				if (findProcessInstances.size()>0){
					
				           b.append(String.format("%sV%s,%s\n", def.getName(),def.getVersion(), findProcessInstances.size() ));
				           
				}
			}
			
			String back = b.toString();
			back = back.substring(0,b.toString().length()-1); // removes last \n
	
			return back ;
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

 }
	
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/stats/instances/active/graph")
	@Produces("text/plain")
	public String getProcessStatsInstancesActiveforGraph() {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		
		StringBuilder b = new StringBuilder();
		b.append("Categories,Number of Instances,Process\n");
		
		int activeInstances=0;
		
		try {
			
			List<ProcessDefinition> processDefinitions = ctx.getGraphSession().findAllProcessDefinitions();
			
			

			for (ProcessDefinition def : processDefinitions) {
				
				List<ProcessInstance> findProcessInstances = ctx.getGraphSession().findProcessInstances(def.getId());
				
				if (findProcessInstances.size()>0){
					
				          
				           
				           for (ProcessInstance processInstance : findProcessInstances) {
				        	   
				        	   if (! processInstance.hasEnded()) {
				        		   
				        		   activeInstances++;
				        	   }
							
						}
				           b.append(String.format("%sV%s,%s\n", def.getName(),def.getVersion(), activeInstances ));
				           
				     activeInstances=0;      
				           
				}
			}
			
			String back = b.toString();
			back = back.substring(0,b.toString().length()-1); // removes last \n
	
			return back ;
 		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			ctx.close();
		}
		return "ERROR";

 }
	
	
	
	@GET
	@Path("/signal/process/{id}/{user}")
	@Produces("text/plain")
	public String signalProcess(
			@PathParam("id")String id, 
			@PathParam("user")String user) {
	      JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
	 
	        try{
	        GraphSession graphSession = ctx.getGraphSession();
	        ProcessInstance processInstance = graphSession.loadProcessInstance((new Long(id)).longValue());
             
	        processInstance.signal();
	        
	        ctx.save(processInstance);
	       
	        
	        return processInstance.getRootToken().getNode().getName(); 
	        
	        } catch (Exception e ) {
	        	e.printStackTrace();
	        	return "ERROR";
	        }
	        finally {
	        	ctx.close();
	        }
	}
	
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/definitions")
	@Produces("application/xml")
	public String getAllProcessDefinitions() {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
 		XStream xstream = new XStream();
 		xstream.alias("process-definition", org.jbpm.graph.def.ProcessDefinition.class);
		List list;  
 		
		try{
		list = ctx.getGraphSession().findAllProcessDefinitions();
 	 		} finally {
 			ctx.close();
 		}
 
		return xstream.toXML(list);

	}
	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/definitions")
	@Produces("application/json")
	public String getAllProcessDefinitionsAsJSON() {
		
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		XStream xstream = new XStream(new JettisonMappedXmlDriver());
 		xstream.alias("process-definition", org.jbpm.graph.def.ProcessDefinition.class);
		List list;  
 		
		try{
		list = ctx.getGraphSession().findAllProcessDefinitions();
 	 		} finally {
 			ctx.close();
 		}
		return xstream.toXML(list);

	}
	
	
	
	@POST
	@Path("/deploy/process/jar")
	@Produces("text/plain")
	public String deployProcessJAR(@QueryParam("f")String fileName) {
		String jbossRunningDir =   Thread.currentThread().getContextClassLoader().getResource("/web/index.jsp").getPath();
		jbossRunningDir =   jbossRunningDir.substring(0, jbossRunningDir.lastIndexOf("/tmp/"));
		try {
			FileCopy.copy(fileName, jbossRunningDir+"/deploy");
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return "OK";
		

	}
	
	
	
	

	

	@GET
	@Path("/status/{id}")
	@Produces("text/plain")
	public String processStatus(@PathParam("id")
	String id) {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));

			Token t = processInstance.getRootToken();
					
			return processInstance.getContextInstance().getVariables()
					.toString()
					+ t.getNode().getName() ;
		} finally {
			ctx.close();
		}

	}
	
    @SuppressWarnings("unchecked")
	@GET
	@Path("/tasks/inbox/{actor}")
	@Produces("text/plain")
    public String tasksByActor(@PathParam("actor") String actor)
    {
        JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
        String s;
        try{
        

        Collection taskList = null;        
        taskList = ctx.getTaskList(actor);
        
        StringBuilder b = new StringBuilder();
        for(Iterator iterator = taskList.iterator(); iterator.hasNext();)
        {
            TaskInstance taskInstance = (TaskInstance)iterator.next();

            if(taskInstance.getStart()!= null)
                b.append(String.format("User %s, the Task: %s [%s] is already started. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), 
                    taskInstance.getName(), 
                    Long.valueOf(taskInstance.getId()), 
                    Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
            else
            if(taskInstance.hasEnded())
                b.append(String.format("User %s, the Task: %s [%s] is ended. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), taskInstance.getName(), Long.valueOf(taskInstance.getId()), Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
            else
                b.append(String.format("User %s, the Task: %s [%s] is waiting for Starting. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), taskInstance.getName(), Long.valueOf(taskInstance.getId()), Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
        }

        s = b.toString();
       
        return s; 
        
        }
        finally {
        	ctx.close();
        }

    }

    
    @SuppressWarnings("unchecked")
	@GET
	@Path("process/instance/{id}/tasks/inbox")
	@Produces("text/plain")
    public String tasksByProcessInstance(@PathParam("id") String id)
    {
    	JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
    	String s;
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));

        Collection taskList = null;  
        
        taskList = ctx.getTaskMgmtSession().findTaskInstancesByProcessInstance(processInstance);
        
        StringBuilder b = new StringBuilder();
        for(Iterator iterator = taskList.iterator(); iterator.hasNext();)
        {
            TaskInstance taskInstance = (TaskInstance)iterator.next();

            if(taskInstance.getStart()!= null)
                b.append(String.format("User %s, the Task: %s [%s] is already started. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), 
                    taskInstance.getName(), 
                    Long.valueOf(taskInstance.getId()), 
                    Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
            else
            if(taskInstance.hasEnded())
                b.append(String.format("User %s, the Task: %s [%s] is ended. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), taskInstance.getName(), Long.valueOf(taskInstance.getId()), Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
            else
                b.append(String.format("User %s, the Task: %s [%s] is waiting for Starting. This is a task from the Process Id: %s \n", new Object[] {
                    taskInstance.getActorId(), taskInstance.getName(), Long.valueOf(taskInstance.getId()), Long.valueOf(taskInstance.getProcessInstance().getId())
                }));
        }

        s = b.toString();
       
        return s; 
        
        }
        finally {
        	ctx.close();
        }

    }


	@SuppressWarnings("unchecked")
	@GET
	@Path("/process/{id}/tasks")
	@Produces("text/plain")
	public String processTaskStatus(@PathParam("id")
	String id) {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));
			
		
		
			Collection<TaskInstance> taskList = null;

			taskList = (Collection<TaskInstance>) processInstance.getTaskMgmtInstance().getTaskInstances();
			
		
			
			StringBuilder b = new StringBuilder();
			
			for (TaskInstance taskInstance : taskList) {
				
				b.append( String.format("I am waiting for Task %s created in %s ", taskInstance.getName(), taskInstance.getCreate())  );
			}
				
			return b.toString();

		} finally {
			ctx.close();
		}

	}	
	
	
	

	@GET
	@Path("/task/{idtask}/{action}")
	@Produces("text/plain")
	public String executeTarefa(@PathParam("idtask")Long taskId,  @PathParam("action")String action) {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			
			TaskInstance task = ctx.loadTaskInstance(new Long(taskId));
			
			if ("start".equalsIgnoreCase(action)) {
				
				task.start();
			}
			
           if ("end".equalsIgnoreCase(action)) {
				
				task.end();
				
			}
		
				
			return String.format("task %s has %sed",task.getId(),action);

		} finally {
			ctx.close();
		}

	}	
	

	
	@SuppressWarnings("unchecked")
	//@GET
	//@Path("/realize/vistoria/{id}/{idsefa}")
	//@Produces("text/plain")
	public String doAddVariable(@PathParam("id")
	String id, @PathParam("idsefa") String idSefa) {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));
		
			if (processInstance.getContextInstance().getVariable("idsefa")==null) {
				
				return "POR FAVOR FACA A PAGAMENTO NA SEFA";
			}

			Collection<TaskInstance> taskList = null;

			taskList = (Collection<TaskInstance>) processInstance.getTaskMgmtInstance().getTaskInstances();
			
			taskList.iterator().next();
			
			taskList.iterator().next().end();
			
			processInstance.getContextInstance().setVariable("idvistoria",  System.currentTimeMillis()+1L );
				
			
			return processInstance.getContextInstance().getVariable("idvistoria").toString() ;

		} finally {
			ctx.close();
		}

	}	
	
	
	@SuppressWarnings("unchecked")
	@GET
	@Path("/realize/entrega/documento/{id}")
	@Produces("text/plain")
	public String doEntregaDocumento(@PathParam("id")
	String id) {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));
		
			if (processInstance.getContextInstance().getVariable("idvistoria")==null) {
				
				return "POR FAVOR ENTREGUE O ID DA VISTORIA";
			}

			Collection<TaskInstance> taskList = null;

			taskList = (Collection<TaskInstance>) processInstance.getTaskMgmtInstance().getTaskInstances();
			
			taskList.iterator().next();
			taskList.iterator().next();

			
			


			taskList.iterator().next().end();
			
			processInstance.getContextInstance().setVariable("identrega",  System.currentTimeMillis()+1L );
				
			
			return processInstance.getContextInstance().getVariable("identrega").toString() ;

		} finally {
			ctx.close();
		}

	}	
	@GET
	@Path("/status/image/{id}")
	@Produces("image/jpg")
	public byte[] statusImage(@PathParam("id")
	String id) throws Exception {

		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			GraphSession graphSession = ctx.getGraphSession();
			ProcessInstance processInstance = graphSession
					.loadProcessInstance(new Long(id));

			ProcessDefinition processDefinition = ctx.getGraphSession()
					.loadProcessDefinition(
							processInstance.getProcessDefinition().getId());

			FileDefinition fileDefinition = processDefinition
					.getFileDefinition();
			if (fileDefinition == null) {
				fileDefinition = new FileDefinition();
				processInstance.getProcessDefinition().addDefinition(
						fileDefinition);
			}
			fileDefinition
					.addFile(
							"processimage.jpg",
							new FileInputStream(
									"/opt/java/eclipse/workspace/carlic/src/main/jpdl/licenciamento/processimage.jpg"));
			fileDefinition
					.addFile(
							"processdefinition.xml",
							new FileInputStream(
									"/opt/java/eclipse/workspace/carlic/src/main/jpdl/licenciamento/processdefinition.xml"));
			fileDefinition
					.addFile(
							"gpd.xml",
							new FileInputStream(
									"/opt/java/eclipse/workspace/carlic/src/main/jpdl/licenciamento/gpd.xml"));

			ctx.getGraphSession().saveProcessDefinition(processDefinition);

			byte[] bytes = fileDefinition.getBytes("processimage.jpg");

			return bytes;

		} finally {
			ctx.close();
		}

	}

	@GET
	@Path("/updateprocess/{processname}")
	@Produces("text/plain")
	public String updateProcess(@PathParam("processname") String processName) {
		JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
		try {
			processDefinition = ProcessDefinition
					.parseXmlResource(String.format("%s/processdefinition.xml",processName));
			ctx.deployProcessDefinition(processDefinition);
			return processName+" has been deployed/updated!".toUpperCase();

		} finally {
			ctx.close();
		}

	}
	
	
	


}